/**
 *
 *      ___                       ___
 *     /\  \          ___        /\  \
 *    /::\  \        /\  \      /::\  \
 *   /:/\:\  \       \:\  \    /:/\ \  \
 *  /:/  \:\  \      /::\__\  _\:\~\ \  \
 * /:/__/ \:\__\  __/:/\/__/ /\ \:\ \ \__\
 * \:\  \  \/__/ /\/:/  /    \:\ \:\ \/__/
 *  \:\  \       \::/__/      \:\ \:\__\
 *   \:\  \       \:\__\       \:\/:/  /
 *    \:\__\       \/__/        \::/  /
 *     \/__/                     \/__/
 *
 * CIS - Clang Indexing Service is a Project Manager & Source code indexing service
 * created so as to simplify the indexing of C/C++ based source code which use
 * standardized build mechanisms.
 *
 **/

#include "../includes/request.hxx"
#include "../includes/projectdirectory.hxx"
#include "../includes/cisexception.hxx"
#include "../includes/requestprocessor.hxx"

#include <sstream>

#include <Poco/Util/OptionSet.h>
#include <Poco/Util/OptionProcessor.h>
#include <Poco/StringTokenizer.h>
#include <Poco/Util/OptionException.h>

using namespace cis;
using namespace Poco;
using namespace Poco::Dynamic;
using namespace Poco::Util;

typedef Project::ProjectPtr ProjectPtr;

namespace {
Poco::Util::OptionSet getCreateOptions();

static const Poco::Util::OptionSet createOptions = getCreateOptions();

Poco::Util::OptionSet getCreateOptions() {
    Poco::Util::OptionSet options;
    options.addOption(Poco::Util::Option("projectname", "n", "project name", true,
            "projectname", true).repeatable(false));
    options.addOption(Poco::Util::Option("makefile", "m", "makefile full path", true,
            "makefile", true).repeatable(false));
    options.addOption(Poco::Util::Option("makeparams", "p", "makefile parameter string", true,
            "makeparams", true).repeatable(false));
    return options;
}

std::string projectCreated = "Project Created";

std::string getJsonSimple(const std::string& result) {
    std::stringstream returnval;
    returnval << "{\"result\" : \"" << result << "\", \"error\" : null, \"id\" : null}" << std::endl;
    return returnval.str();
}

static bool registered = RequestProcessor::addCommand<CreateProjectRequest>("CreateProjectRequest");

void unquote(std::string& in) {
    char a = in[0];
    char b = in[in.length() - 1];
    if (a == b && a == '"')
        in = in.substr(1, in.length() - 2);
}

std::string trim(const std::string& in) {
    std::string tmp = in;
    tmp.erase(tmp.begin(), std::find_if(tmp.begin(), tmp.end(), std::not1(std::ptr_fun<int, int>(std::isspace))));
    tmp.erase(std::find_if(tmp.rbegin(), tmp.rend(), std::not1(std::ptr_fun<int, int>(std::isspace))).base(), tmp.end());
    if (tmp != "")
        unquote(tmp);
    return tmp;
}

}

Request::Request() {
}

std::vector<std::string> Request::splitOptions(const std::string& params) {
    int idx = 0, idxStart = 0;
    std::string tmp;
    std::vector<std::string> ret;
    int plen = params.length();
    bool inquotes = false;
    while (idx != plen) {
        if (params[idx] == '\\') {
            ++idx;
            if (idx != plen)
                ++idx;
            else {
                tmp = trim(params.substr(idxStart, idx));
                if (tmp != "")
                    ret.push_back(tmp);
                return ret;
            }
        } else if (params[idx] == '\"') {
            inquotes = !inquotes;
        } else if (params[idx] == ' ' && !inquotes) {
            tmp = trim(params.substr(idxStart, idx - idxStart));
            if (tmp != "")
                ret.push_back(tmp);
            idxStart = idx;
        }
        ++idx;
    }
   tmp = trim(params.substr(idxStart, idx));
    if (tmp != "")
        ret.push_back(tmp);
    return ret;
}

void Request::processOptions(const std::string& params) {
    std::vector<std::string> paramTokens = splitOptions(params);
    Poco::Util::OptionProcessor processor(getOptions());
    std::vector<std::string>::iterator it = paramTokens.begin();
    std::string name, value;
    try {
        while (it != paramTokens.end()) {
            processor.process(*it, name, value);
            if (!name.empty())
                optionValueMap[name] = value;
            ++it;
        }
        processor.checkRequired();
    } catch (Poco::Util::OptionException& e) {
        throw cis::InvalidRequestException("Incompatible Option - " + e.message());
    }
}

const Poco::Util::OptionSet& CreateProjectRequest::getOptions() {
    return createOptions;
}

CreateProjectRequest::CreateProjectRequest() {
}

std::string CreateProjectRequest::execute(const std::string& params) {
    processOptions(params);
    ProjectPtr p = ProjectDirectory::instance().createProject(optionValueMap["projectname"],
            optionValueMap["makefile"], optionValueMap["makeparams"]);
    return getJsonSimple(projectCreated);
}

